package authorization

import (
	"cloud_android_backend/pkg/middleware/authorization/hmac"
	"cloud_android_backend/pkg/middleware/authorization/jwt"
	model "cloud_android_backend/pkg/models/users"
	modules "cloud_android_backend/pkg/modules/users"
	"fmt"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"github.com/jinzhu/gorm"
	"k8s.io/klog/v2"
	"modules.tenxcloud.com/common/composite"
	"modules.tenxcloud.com/common/utility/errors"
)

const (
	LoginUserKey = "login-user"
)

func TokenCheck(c *gin.Context) {
	rawToken := c.Request.Header.Get("Authorization")
	if rawToken == "" {
		glog.Warning("unauthorized access, token not specified")
		c.AbortWithStatusJSON(errors.Unauthorized("token should be specified in header with 'Authorization' key"))
		return
	}
	u := model.New()
	// 如果是ak/sk认证
	if hmac.IsSupportedSignAlgorithm(rawToken) {
		err := hmacVerify(c.Request, u)
		if err != nil {
			glog.Warning(err)
			c.AbortWithStatusJSON(errors.Unauthorized("validate secret key failed"))
		}
	} else {
		var username, token string
		ok, bearerToken := composite.JWTAuthorizer.IsBearerToken(rawToken)
		if ok {
			loginInfo, err := jwt.Validate(bearerToken)
			if err != nil {
				if composite.JWTAuthorizer.IsTokenExpired(err) {
					glog.Warning("unauthorized access, bearer token expired")
					c.AbortWithStatusJSON(errors.Unauthorized("bearer token expired"))
					return
				}
				glog.Warningf("validate bearer token failed, %s", err)
				c.AbortWithStatusJSON(errors.Unauthorized(err))
				return
			}
			username = loginInfo.GetUserName()
		} else {
			username = c.Request.Header.Get("username")
			if username == "" {
				glog.Warning("unauthorized access, username not specified")
				c.AbortWithStatusJSON(errors.Unauthorized("username should be specified in header"))
				return
			}
			if len(rawToken) <= tokenPrefixLength {
				glog.Warning("unauthorized access, invalid token format")
				c.AbortWithStatusJSON(errors.Unauthorized("invalid token format"))
				return
			}
			token = rawToken[tokenPrefixLength:]
		}
		db, err := composite.Database.GetOrm()
		if err != nil {
			glog.Errorf("get db connection for getting user failed, %s", err)
			c.AbortWithStatusJSON(errors.InternalServerError(err))
			return
		}
		//u := model.New()
		if err := u.GetByUsername(db, username); err != nil {
			if gorm.IsRecordNotFoundError(err) {
				glog.Errorf("unauthorized access, user not found, %s", username)
				c.AbortWithStatusJSON(errors.Unauthorized("user not found"))
				return
			}
			glog.Errorf("get user from db failed, user %s, %s", username, err)
			c.AbortWithStatusJSON(errors.InternalServerError(err))
			return

		}
		if token != "" {
			if u.ApiToken != token {
				c.AbortWithStatusJSON(errors.Unauthorized("unauthorized access, invalid token"))
				return
			}
		}
	}

	clientIP := c.Request.Header.Get("X-Forwarded-For")

	if len(clientIP) == 0 {
		clientIP = c.Request.RemoteAddr // RemoteAddr: "192.168.1.1:8080"
	} else {
		index := strings.Index(clientIP, ",")
		if index > 0 {
			clientIP = clientIP[0:index]
		}
	}
	u.ClientIP = clientIP
	//TODO getting the location
	u.Region = "未知"
	c.Set(LoginUserKey, u)
	c.Next()
}

const tokenPrefixLength = len("token ")

func AdminCheck(c *gin.Context) {
	permissionCheck(c, "admin")
}

func permissionCheck(c *gin.Context, typeCheck string) {
	li, exists := c.Get(LoginUserKey)
	if !exists {
		c.AbortWithStatusJSON(errors.Unauthorized("get user from content failed"))
		return
	}
	klog.Info(li)
	u, _ := li.(modules.User)
	klog.Info(u)
	if u.GetType() != typeCheck {
		c.AbortWithStatusJSON(errors.Forbidden(fmt.Sprintf("user %s type check failed", u.GetUserName())))
		return

	}
	c.Next()
}

func hmacVerify(request *http.Request, u *model.User) error {
	authorization := request.Header.Get("authorization")
	authValue, err := hmac.ExtractAuthField(authorization)
	if err != nil {
		return err
	}
	db, err := composite.Database.GetOrm()
	if err != nil {
		return err
	}
	userCredential, err := modules.GetUserByAccessKey(db, authValue[hmac.AuthFieldAccess])
	if err != nil {
		return err
	}
	*u = userCredential.User
	if userCredential.SecretKey == "" {
		return fmt.Errorf("get user secretKey failed")
	}
	err = hmac.VerifySign(request, userCredential.SecretKey)
	if err != nil {
		return err
	}
	return nil
}
